﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Serialization;

namespace d39
{
    [Serializable]
    public class PositionMove
    {
        public PositionMove()
            : this(null, default(Coordinate), null) { }

        public PositionMove(Player p, Coordinate position, List<Vector> moves)
        {
            Player = p;
            Position = position;
            Moves = moves ?? new List<Vector>();
            Layers = new LayerSet(1);
        }

        [XmlIgnore]
        public Player Player { get; set; }

        [XmlIgnore]
        public Coordinate Position { get; set; }

        [XmlAttribute("position")]
        public string PositionString
        {
            get { return Position.ToHumanReadableString(); }
            set { Position = Coordinate.ParseHumanReadable(value); }
        }

        [XmlIgnore]
        public List<Vector> Moves { get; set; }

        [XmlAttribute("moves")]
        public string MovesString
        {
            get { return string.Join(" ", from move in Moves select d39.Moves.ToHumanReadableMoveString(move)); }
            set { Moves = (from move in value.Split(' ') where !string.IsNullOrEmpty(move) select d39.Moves.ParseHumanReadable(move)).ToList(); }
        }

        [XmlIgnore]
        public LayerSet Layers { get; set; }

        [XmlAttribute("layers")]
        public string LayersString
        {
            get { return Layers.ToXmlString(); }
            set { Layers = LayerSet.FromXmlString(value); }
        }

        [XmlAttribute("inactive")]
        public bool IsInactive { get; set; }

        [XmlAttribute("invisible")]
        public bool IsInvisible { get; set; }

        [XmlAttribute("unknown")]
        public bool IsUnknown { get; set; }

        [XmlIgnore]
        public bool IsFixed { get { return (Player.IsFixed || IsInactive) && !Moves.Any(); } }

        public string GetMoveString(DnDVersion ver)
        {
            return Player.Name + (IsInvisible ? " (invisible)" : "") + (IsInactive ? " (inactive)" : "") + GetJustMoveString(ver);
        }

        public string GetJustMoveString(DnDVersion ver)
        {
            return " moves " +
                Moves.ToHumanReadableMoveString(ver) + " to " + Position.ToHumanReadableString();
        }

        public override string ToString()
        {
            return ToString(UrlTweaks.Default);
        }

        public string ToHumanReadablePositionString()
        {
            var posString = Position.ToHumanReadableString() + (IsUnknown ? "??" : "");
            return IsInvisible ? "«" + posString + "»" : posString;
        }
        public string ToFullHumanReadablePositionString()
        {
            return IsInactive ? "(" + ToHumanReadablePositionString() + ")" : ToHumanReadablePositionString();
        }

        // Format:
        // Fxymmmm
        // F = flags, if applicable (1 = Inactive, 2 = Invisible, 3 = both); missing indicates neither
        // xy = coordinate
        // mmmm = move, if any
        public string ToString(UrlTweaks tweaks)
        {
            var result = Position.ToString();
            var flags = (IsInactive ? 1 : 0) + (IsInvisible ? 2 : 0) + (IsUnknown ? 4 : 0);
            if (flags != 0) result = flags + result;
            if (tweaks.IncludeMoves) result += Moves.ToMoveString();
            return result;
        }

        public static PositionMove Parse(Player p, DnDVersion ver, string str)
        {
            int flags = 0;
            int strIndex = 0;
            if (str[strIndex] >= '1' && str[strIndex] <= '7')
            {
                flags = int.Parse("" + str[strIndex++]);
            }
            var coord = Coordinate.Parse(str, strIndex, out strIndex);
            var moves = new List<Vector>();
            if (strIndex < str.Length)
            {
                if (ver == DnDVersion.V40 && str[strIndex] == '0')
                {
                    //isShift = true; no longer supported, it never really worked
                    strIndex++;
                }
                moves.AddRange(d39.Moves.FromMoveString(str.Substring(strIndex)));
            }
            return new PositionMove(p, coord, moves) { IsInactive = (flags & 1) != 0, IsInvisible = (flags & 2) != 0, IsUnknown = (flags & 4) != 0 };
        }
        public static List<PositionMove> ParseMulti(Player p, DnDVersion ver, string str)
        {
            if (str == "") return new List<PositionMove>();
            return (from part in str.Split('.') select Parse(p, ver, part)).ToList();
        }
    }
}